<script setup lang="ts">
import * as echarts from 'echarts'
import { ECharts, EChartsOption } from 'echarts'
import { EChartsUtil, getLinesStyle, outlineLineFormatter } from '@utils/echarts'
import { EmptyEnum, EmptyState } from '@yk/components/empty-box/type'
import { PreferenceDB } from '@utils/indexedDB/preference'
import YkEmptyBox from '@yk/components/empty-box/YkEmptyBox.vue'
import { Download, InfoFilled } from '@element-plus/icons-vue'
import { DateFormat, moment } from '@utils/moment'
import { FnOperationControl } from '@utils/object-util/periodic-execution/FnOperationControl'
import { ObjectUtil } from '@utils/object-util'
import { getShowVariableListByDeviceIdAPI } from '@/api/variable'
import { DeviceDTO } from '@/api/entity/device'
import { VariableDTO } from '@/api/entity/variable'
import { getRealDataAPI, getRealDataWebSocketAPI } from '@/api/realtime'
import { getVariableHistoryByDateAPI } from '@/api/history'
import { Canceler } from 'axios'
import { controlDeviceVariableAPI } from '@/api/device'
import YkPagination from '@yk/components/pagination/YkPagination.vue'
import { TableUtil } from '@utils/table-util'
import { Pages } from '@typings/pages'
import { DeviceRoleUtil } from '@/dataset/device-role'
import { ServerEnum } from '@/api/enum/server'
import YkTableColumn from '@yk/components/table-column/YkTableColumn.vue'
import { MountedCheck } from '@/utils'
import { WebSocketPlus } from '@utils/websocket/WebSocketPlus'

const router = useRouter()

defineOptions({ name: 'DeviceRealtime' })
const props = defineProps<{
  device: DeviceDTO
}>()

const mc = new MountedCheck()
onMounted(() => {
  initTodayLine()
  initData()
  mc.end()
})
onActivated(() => {
  if (mc.isMounted()) return
  // 如果在非激活时切换了设备，激活时会触发watch
  connectWS()
  if (currentVariable.value) fnOperationControl.run()
})
onDeactivated(() => {
  disconnectWS()
  fnOperationControlStop()
})
onBeforeUnmount(() => {
  disconnectWS()
  fnOperationControlStop()
})

// region TODO 实时数据 - 变量列表
type IndicatesStatusDicType = Record<string, { color: string, title: string }>
type VariableListItem = VariableDTO & { indicatesStatusDic?: IndicatesStatusDicType }
const variableList = shallowRef<VariableListItem[]>([])
const currentVariable = ref<VariableListItem>(null)
const variableListState = shallowRef<EmptyState>(EmptyEnum.NONE)

let currentVariableDBKey = ''
// 获取变量列表
function getVariableList () {
  variableListState.value = EmptyEnum.LOADING
  getShowVariableListByDeviceIdAPI({
    deviceId: props.device.id
  }).then((res) => {
    res.result().then((rdata) => {
      const retData: VariableListItem[] = rdata.data || []
      retData.forEach((item) => {
        if (item.indicatesStatus) {
          item.indicatesStatusDic = item.indicatesStatus.reduce<IndicatesStatusDicType>((ret, item1) => {
            ret[item1.statusName] = { title: item1.statusValue, color: item1.colour }
            return ret
          }, {})
        }
      })
      variableList.value = retData
      if (variableList.value.length) {
        variableListState.value = EmptyEnum.SUCCESS
        getPreferenceVariable()
      } else {
        variableListState.value = EmptyEnum.EMPTY
      }
    }).catch(() => {
      variableListState.value = EmptyEnum.EMPTY
    })
  }).catch(() => {
    variableListState.value = EmptyEnum.EMPTY
  })
  // console.log('获取变量列表')
  // if (props.device.id === 1) {
  //   variableList.value = [
  //     { id: 1, label: '电压', isHistory: true, isControl: false, key: 'voltage', unit: 'V' },
  //     { id: 3, label: '频率', isHistory: false, isControl: false, key: 'frequency', unit: 'Hz' }
  //     // { id: 5, label: '流量', isHistory: false, isControl: false }
  //   ]
  //   variableListState.value = EmptyEnum.SUCCESS
  // } else if (props.device.id === 2) {
  //   variableList.value = [
  //     { id: 2, label: '电流', isHistory: false, isControl: false, key: 'electricity', unit: 'A' }
  //     // { id: 4, label: '流量', isHistory: false, isControl: false }
  //   ]
  //   variableListState.value = EmptyEnum.SUCCESS
  // } else {
  //   variableList.value = []
  //   variableListState.value = { type: EmptyEnum.EMPTY, description: '没有可展示的变量' }
  // }
}
// 获取当前设备的偏好变量
function getPreferenceVariable () {
  PreferenceDB.DeviceData.getItem(currentVariableDBKey).then((e) => {
    if (e) {
      const result = variableList.value.find(item => item.id === e)
      if (result) variableCardClickHandle(result)
    }
  })
}
// 点击实时数据卡片点击实现
function variableCardClickHandle (data: VariableListItem) {
  if (data.storage === '1') {
    setCurrentVariable(data)
  }
}
// 设置当前选中变量
function setCurrentVariable (data: VariableListItem) {
  currentVariable.value = data
  if (currentVariable.value) {
    PreferenceDB.DeviceData.setItem(currentVariableDBKey, data.id)
    todayLineState.value = EmptyEnum.SUCCESS
    fnOperationControl.run()
  } else {
    fnOperationControlStop()
    todayLineState.value = EmptyEnum.EMPTY_VAR
  }
}

// endregion

// region TODO 初始化数据
function initData () {
  setCurrentVariable(null)
  currentVariableDBKey = `realtime_${props.device.id}_currentVariable`
  realtime.value = {}
  disconnectWS()

  getVariableList()
  getRealtimeData()
  connectWS()
}
// endregion

// region TODO 实时数据 - 数据
// 实时数据
const realtime = shallowRef({})
const realDataTime = ref('')
function getRealtimeData () {
  // 如果实时数据为空，则获取一次数据，弥补websocket下次推送的空挡
  if (!Object.keys(realtime.value).length) {
    getRealDataAPI({
      tableName: props.device.id
    }).then((res) => {
      res.result().then((rdata) => {
        // 如果中途实时数据已获取，如websocket推送了，则以websocket为优先
        if (!Object.keys(realtime.value).length) {
          realtime.value = rdata.data?.lastData || {}
          realDataTime.value = rdata.data?.ts
        }
      })
    })
  }
}
// 处理后显示的值
const showRealTimeData = computed(() => {
  return variableList.value.reduce<Record<string, { value: string, error: string, color: string }>>((ret, item) => {
    const data = { value: '', error: '', color: '#1570BB' }
    const value = realtime.value[item.address]
    if (value === 'NaN') {
      data.error = '数据错误，请检查设备状态或计算公式是否有误'
      data.value = '--'
      ret[item.address] = data
      return ret
    }
    if (item.type === '0') {
      const value = realtime.value[item.address]
      if (ObjectUtil.isEmpty(value)) data.value = '--'
      else if (ObjectUtil.isNotEmpty(item.readMax) && value > item.readMax) {
        data.value = item.readMax.toString()
        data.error = '超限：' + value
      } else if (ObjectUtil.isNotEmpty(item.readMin) && value < item.readMin) {
        data.value = item.readMin.toString()
        data.error = '超限：' + value
      } else {
        data.value = value.toString()
      }
    } else {
      data.value = item.indicatesStatusDic[value]?.title || ObjectUtil.isEmptyToVal(value, '--')
      data.color = item.indicatesStatusDic[value]?.color || '#1570BB'
    }
    ret[item.address] = data
    return ret
  }, {})
})
// endregion

// region TODO 实时数据 - WebSocket
let ws: WebSocketPlus = null
const isConnected = shallowRef(false)
// 当前连接的设备Id
let connectDeviceId = null
// 连接
function connectWS () {
  // 避免重复连接
  if (ws) {
    // 如果当前连接已连接，则不再连接
    if (connectDeviceId === props.device.id) return
    else disconnectWS()
  }

  // 记录当前连接
  connectDeviceId = props.device.id
  ws = getRealDataWebSocketAPI({ deviceId: props.device.id })
  ws.onMessage((e) => {
    try {
      const data = JSON.parse(e.data || '[]')
      realtime.value = data.reduce((ret, item) => {
        ret[item.address] = item.value
        return ret
      }, {})
      realDataTime.value = moment(data[0].createdAt).format(DateFormat.DateTime)
    } catch (e) {
    }
  }).onState((ev) => {
    isConnected.value = ev.connect
  })
}
// 断开连接
function disconnectWS () {
  if (ws) {
    ws && ws.close()
    ws = null
  }
}
// endregion

// region TODO 控制
const controlVariable = shallowRef<VariableDTO>()
const controlDialogModel = shallowRef(false)
const controlValue = shallowRef()
function showControlDialogModel (variable: VariableDTO) {
  if (!DeviceRoleUtil.isControl(props.device.id)) {
    ElMessage.info('您没有对设备的控制权限')
    return
  }
  controlDialogModel.value = true
  controlVariable.value = variable
}
function closeControlDialogModel () {
  controlDialogModel.value = false
  controlValue.value = null
}
function submitControl () {
  ElMessageBox.confirm('确认下发该指令？', '下发提醒').then(() => {
    if (ObjectUtil.isEmpty(controlValue.value)) {
      ElMessage.info('下发值不可为空，请输入下发值')
      return
    }
    if (controlVariable.value.type === '0') {
      const { writeMax, writeMin } = controlVariable.value
      if (ObjectUtil.isNotEmpty(writeMax) && controlValue.value > writeMax) {
        ElMessage.info('超过可写入最大值：' + writeMax)
        return
      }
      if (ObjectUtil.isNotEmpty(writeMin) && controlValue.value < writeMin) {
        ElMessage.info('超过可写入最小值：' + writeMin)
        return
      }
    }
    const loading = ElLoading.service()
    controlDeviceVariableAPI({
      deviceId: props.device.id,
      variableId: controlVariable.value.id,
      param: controlValue.value
    }).then((res) => {
      res.result().then(() => {
        ElMessage.success('命令已下发')
        closeControlDialogModel()
      })
    }).finally(loading.close)
  })
}
// endregion

// region TODO 今日折线图
const todayLineRef = shallowRef<HTMLDivElement>()
const todayLine = shallowRef<ECharts>()
const todayLineState = shallowRef<EmptyState>(EmptyEnum.NONE)
function initTodayLine () {
  const xAxis2Data = []
  for (let i = 0; i <= 24; i += 2) {
    xAxis2Data.push(i.toString().padStart(2, '0') + ':00')
  }
  todayLine.value = echarts.init(todayLineRef.value)
  todayLine.value.setOption(getLinesStyle({
    colors: ['#1570BB', '#FC5555'],
    names: ['今日数据', '设备离线'],
    area: true
  }).setLegend({ show: false })
    .addXAxis({ type: 'category', position: 'bottom', data: xAxis2Data, axisPointer: { show: false } })
    .setXAxis({ axisLabel: { show: false } })
    .setTooltip({ formatter: outlineLineFormatter }).getResult())
  EChartsUtil.addResizeListener(todayLine.value)
}

let todayLineDataCanceler: Canceler = null
async function getTodayLineData (option) {
  if (option.isFirstRun) todayLineState.value = EmptyEnum.LOADING
  const params = {
    tableName: props.device.id,
    variableIds: [currentVariable.value.id],
    startTime: moment().format(DateFormat.Date) + ' 00:00:00',
    endTime: moment().format(DateFormat.DateTime)
  }
  exportParams.value = params
  return getVariableHistoryByDateAPI(params, {
    cancelHook (canceler) {
      todayLineDataCanceler = canceler
    }
  }).then((res) => {
    res.result().then((rdata) => {
      rdata.data ??= []
      const data = rdata.data[0]?.variableVOList || []
      if (data.length) {
        const today = moment().format(DateFormat.Date)
        todayLine.value.setOption({
          yAxis: [{ name: currentVariable.value.unit }],
          xAxis: [{
            min: today + ' 00:00:00',
            max: today + ' 23:59:59'
          }],
          series: [
            { data: ObjectUtil.copy(data).map(item => [item.ts, item.value]) }
          ]
        } as EChartsOption)
        todayLineState.value = EmptyEnum.SUCCESS
        getTableData(data.reverse())
      } else {
        todayLineState.value = EmptyEnum.EMPTY
        getTableData([])
      }
    })
  })
  // 模拟数据
  // const data = [
  //   ['2020-12-12 2:12:17', 2],
  //   ['2020-12-12 3:12:17', 10],
  //   ['2020-12-12 4:12:17', 17],
  //   ['2020-12-12 5:12:17', 12],
  //   ['2020-12-12 06:00:00', 12],
  //   ['2020-12-12 12:12:12', 12],
  //   ['2020-12-12 12:12:13', 14],
  //   ['2020-12-12 14:12:13', 16],
  //   ['2020-12-12 14:12:16', 12],
  //   ['2020-12-12 17:12:13', 10],
  //   ['2020-12-12 17:15:13', 12]
  // ]
  // // 模拟上下线数据
  // const offLineData = [
  //   { time: '2020-12-12 2:12:11', type: 1 },
  //   { time: '2020-12-12 06:00:00', type: 0 },
  //   { time: '2020-12-12 08:00:00', type: 1 },
  //   { time: '2020-12-12 15:12:11', type: 0 },
  //   { time: '2020-12-12 17:12:11', type: 1 }
  // ]
  // if (offLineData.length && offLineData[0].type !== 0) {
  //   offLineData.unshift({ time: '2020-12-12 00:00:00', type: 0 })
  // }
  // // 记得封装一个合并离线数据的函数，传入正常数据和离线数据，返回合并好的数据和离线数据（不需要兼容24小时双X轴）
  // // 封装一个单折线和多折线的
  // // 可以的话封装一个普通数据转时间轴数据的函数
  // const data2 = []
  // let j = 0
  // for (let i = 0, len = offLineData.length; i < len; i++) {
  //   const item = offLineData[i]
  //   data2.push([item.time, 0])
  //   if (item.type !== 0) {
  //     data2.push([item.time, null])
  //     continue
  //   }
  //   const time = new Date(item.time).getTime()
  //   for (; j < data.length; j++) {
  //     const time2 = new Date(data[j][0]).getTime()
  //     if (time2 > time) {
  //       if (j !== 0) {
  //         data.splice(j, 0, [time, null])
  //         j++
  //       }
  //       break
  //     }
  //   }
  // }
  // console.log(data, data2)

  // const data = ObjectUtil.copy(DBData)
  // console.log(data)
  // realtime.value = ObjectUtil.copy(DBRealtime)
  // const today = moment().format(DateFormat.Date)
  // todayLine.value.setOption({
  //   xAxis: [{
  //     min: today + ' 00:00:00',
  //     max: today + ' 23:59:59'
  //   }],
  //   series: [
  //     {
  //       data: ObjectUtil.copy(data).map(item => [item.time, item.value])
  //     }
  //     // {
  //     //   data: data2
  //     // }
  //   ]
  // } as EChartsOption)
}
// 周期执行某个函数
const fnOperationControl = new FnOperationControl({ fn: getTodayLineData, timeout: 60000, pause: true })
// 停止执行
function fnOperationControlStop () {
  fnOperationControl.stop()
  // 取消当前请求，避免过慢的请求导致渲染无用的数据（例如请求发出后切换设备，导致旧设备的数据渲染到新设备上）
  todayLineDataCanceler && todayLineDataCanceler()
}
onBeforeUnmount(() => {
  EChartsUtil.removeResizeListener(todayLine.value)
})
// endregion

// region TODO 数据表格
const tableData = ref([])
const pages = ref(new Pages())
const tableDataPool = ref([])

function getTableData (data) {
  tableDataPool.value = data
  pages.value.total = data.length
  getTableDataPage()
}
function getTableDataPage () {
  tableData.value = TableUtil.paging(tableDataPool.value, pages.value)
}
// endregion

// region TODO 导出
const exportParams = shallowRef()
// endregion

// region TODO 监听设备发生变化
watch(() => props.device, (nv, ov) => {
  if (props.device && nv.id !== ov?.id) {
    console.log(1)
    initData()
  }
})
// endregion

// region TODO 折叠
const variableListCollapse = ref(true)
// endregion

</script>

<template>
  <div class="wh-100 d-flex position-relative">
    <!--实时数据-->
    <div class="c_card-white wh-100 variable-box" :style="{ width: variableListCollapse ? '0%' : '100%' }">
      <!--卡片头-->
      <div class="c_card_header">
        <div class="c_card-title d-flex-center">
          <span>实时数据</span>
          <div class="ml-1" :class="[isConnected ? 'bg-color-success' : 'bg-color-info']" style="width: 6px;aspect-ratio: 1;border-radius: 50%;" />
        </div>
        <div class="c_header_right fs-14 color-secondary fw-4">更新时间：{{ realDataTime || '--' }}</div>
      </div>
      <!--内容-->
      <div class="overflow-auto" style="height: var(--card-body-height);">
        <yk-empty-box :state="variableListState" style="height: 100%;">
          <div class="d-grid variable-list">
            <div
              v-for="item in variableList" :key="item.id" class="variable-list__item"
              :class="{ 'is-history': item.storage === '1', 'is-active': currentVariable?.id === item.id }"
              @click="variableCardClickHandle(item)"
            >
              <div>
                <p class="variable-name">{{ item.name }}</p>
                <!--模拟量-->
                <p v-if="item.type === '0'" class="variable-value">
                  <span>{{ showRealTimeData[item.address].value }}</span>
                  <el-popover v-if="showRealTimeData[item.address].error" :content="showRealTimeData[item.address].error">
                    <template #reference>
                      <el-icon class="fs-16 color-info ml-1"><InfoFilled /></el-icon>
                    </template>
                  </el-popover>
                  <span class="variable-unit">{{ item.unit }}</span>
                </p>
                <!--开关量-->
                <p v-else class="variable-value">
                  <span :style="{ color: showRealTimeData[item.address].color }">{{ showRealTimeData[item.address].value }}</span>
                </p>
              </div>
              <div class="d-flex-center color-primary">
                <ic-system-setting1 v-if="item.control === '1'" class="cursor-pointer" @click.stop="showControlDialogModel(item)" />
              </div>
            </div>
          </div>
        </yk-empty-box>
      </div>
      <!--折叠-->
      <collapse-side
        style="z-index: 20;"
        :collapse="variableListCollapse" class="collapse-side" mirroring
        @click="variableListCollapse = !variableListCollapse"
      />
    </div>
    <!--今日数据-->
    <div class="c_card-white flex-1 w-0" style="margin-left: 400px;flex-shrink: 0;">
      <yk-empty-box :state="todayLineState" class="h-100">
        <div class="h-100">
          <div class="c_card_header">
            <div class="c_card-title">今日历史曲线（{{ currentVariable?.name }}）</div>
            <div class="c_header_right">
              <el-link type="primary" @click="router.push({ name: 'DeviceHistoryData' })">更多历史数据</el-link>
            </div>
          </div>
          <div ref="todayLineRef" class="overflow-hidden" style="height: 300px;" />
          <div class="mt-12 overflow-auto position-relative" style="height: calc(100% - 340px)">
            <export-file url="/dataOperation/export" :params="exportParams" :server="ServerEnum.DataGatherer">
              <el-link type="primary" class="position-absolute" style="z-index: 9;right: 16px;top: 12px;">
                <el-icon><Download /></el-icon>
                <span>导出</span>
              </el-link>
            </export-file>
            <el-table :data="tableData" class="c_table-page" stripe>
              <el-table-column label="#" type="index" :index="TableUtil.indexMethod.bind(null, pages)" />
              <el-table-column label="数据时间" prop="ts" />
              <el-table-column label="变量值" prop="value" />
              <yk-table-column label="单位" prop="unit" />
            </el-table>
            <yk-pagination class="c_table-pagination-page position-sticky" style="bottom: 0;z-index: 99" :pages="pages" @handle="getTableDataPage" />
          </div>
        </div>
      </yk-empty-box>
    </div>
    <!--下发控制-->
    <el-dialog
      v-model="controlDialogModel" title="下发控制" width="300px"
      draggable
      :close-on-click-modal="false"
      @close="closeControlDialogModel"
    >
      <div style="margin: -14px 0;">
        <el-form label-width="90px">
          <el-form-item label="变量名称：">
            <span>{{ controlVariable.name }}</span>
          </el-form-item>
          <el-form-item label="下发值：">
            <el-input-number v-if="controlVariable.type === '0'" v-model="controlValue" class="el-c-input-pure" />
            <el-select v-else v-model="controlValue">
              <el-option
                v-for="item in controlVariable.indicatesStatus" :key="item.statusName"
                :value="item.statusName" :label="item.statusValue"
              />
            </el-select>
          </el-form-item>
        </el-form>
      </div>
      <template #footer>
        <div class="">
          <el-button type="primary" plain @click="closeControlDialogModel">取消</el-button>
          <el-button type="primary" @click="submitControl">下发</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<style scoped lang="scss">
// 实时数据
.variable-box {
  position: absolute;
  min-width: 388px;
  transition: width .3s ease-out;
  z-index: 100;
  //变量列表
  .variable-list {
    grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
    grid-gap: 16px;
    .variable-list__item {
      padding: 14px 16px 14px 14px;
      height: 86px;
      background-color: #F7F8FA;
      border-style:solid;
      border-color: #85B4DB;
      border-width: 0 1px 1px 0;
      background-position: right bottom;
      background-repeat: no-repeat;
      background-image: url("@images/card/card-decorate-ornament.png");
      display: flex;
      justify-content: space-between;
      &.is-active {
        border-width: 0 2px 2px 0;
        transition: all 0.1s linear;
        border-color: var(--yk-color-primary-light-3);
        background-color: var(--yk-color-primary-light-9);
        box-shadow: inset 0 0 0 1px var(--yk-color-primary-light-3);
      }
      &.is-history {
        background-image: url("@images/card/card-decorate.png"), url("@images/card/card-decorate-ornament.png");
        cursor: pointer;
      }
      .variable-name {
        color: var(--el-text-color-primary);
        font-size: 14px;
      }
      .variable-value {
        color: var(--el-color-primary);
        font-size: 24px;
        font-weight: bold;
      }
      .variable-unit {
        color: var(--el-color-primary);
        font-size: 14px;
        font-weight: normal;
        margin-left: 8px;
      }
    }
  }
}

// 折叠
.collapse-side {
  position: absolute;
  margin: auto;
  inset: 0 -19px 0 auto;
}
</style>
